package online.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;

import core.exception.PhysicalException;
import core.util.ArrayUtil;

/**
 * フィルタ共通
 *
 * @author Tadashi Nakayama
 */
public final class FilterUtil {

	/**
	 * コンストラクタ
	 */
	private FilterUtil() {
		throw new AssertionError();
	}

	/**
	 * Viewフォーワド確認
	 * @param request リクエスト
	 * @return Viewフォワードの場合 true を返す。
	 */
	public static boolean isView(final HttpServletRequest request) {
		String path = toPlainURI(request.getRequestURI());
		return path.endsWith(".jsp") || path.endsWith(".jspx");
	}

	/**
	 * クエリ文字列キー値集合化
	 * @param query クエリ文字列
	 * @return クエリ文字列キー値集合
	 */
	public static Set<String> toParameterKeySet(final String query) {
		if (!Objects.toString(query, "").isEmpty()) {
			Set<String> ret = new HashSet<>();
			for (final String attr : query.split("&")) {
				String[] vals = attr.split("=", 2);
				if (vals.length == 2) {
					ret.add(vals[0]);
				}
			}
			return ret;
		}
		return Collections.emptySet();
	}

	/**
	 * クエリ文字列マップ化
	 * @param query クエリ文字列
	 * @param encoding エンコーディング
	 * @return マップ
	 */
	public static Map<String, String[]> toParameterMap(final String query, final String encoding) {
		try {
			if (!Objects.toString(query, "").isEmpty()) {
				Map<String, String[]> map = new HashMap<>();
				for (final String param : query.split("&")) {
					String[] vals = param.split("=", 2);
					if (vals.length == 2) {
						map.put(vals[0], ArrayUtil.extend(
								map.get(vals[0]), URLDecoder.decode(vals[1], encoding)));
					}
				}
				return map;
			}
			return Collections.emptyMap();
		} catch (UnsupportedEncodingException ex) {
			throw new PhysicalException(ex);
		}
	}

	/**
	 * キー値をクエリ文字列から除外
	 * @param query クエリ文字列
	 * @param key キー値
	 * @return 除外後文字列
	 */
	public static String stripParameter(final String query, final String... key) {
		StringBuilder sb = new StringBuilder();
		if (!Objects.toString(query, "").isEmpty()) {
			for (final String qry : query.split("&")) {
				if (!isTarget(qry, key)) {
					if (0 < sb.length()) {
						sb.append("&");
					}
					sb.append(qry);
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 対象判断
	 * @param qry 文字列
	 * @param keys キー値
	 * @return qryがキー値で開始している場合 true を返す。
	 */
	private static boolean isTarget(final String qry, final String... keys) {
		if (keys != null) {
			for (final String key : keys) {
				if (qry.startsWith(key + "=")) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * クエリ文字列取得
	 * @param request リクエストオブジェクト
	 * @return クエリ文字列
	 */
	public static String getQueryString(final HttpServletRequest request) {
		if (isInclude(request)) {
			return String.class.cast(request.getAttribute("javax.servlet.include.query_string"));
		}
		return request.getQueryString();
	}

	/**
	 * リクエストURI取得
	 * @param request リクエストオブジェクト
	 * @return リクエストURI
	 */
	public static String getRequestURI(final HttpServletRequest request) {
		if (isInclude(request)) {
			return String.class.cast(request.getAttribute("javax.servlet.include.request_uri"));
		}
		return request.getRequestURI();
	}

	/**
	 * サーブレットパス取得
	 * @param request リクエストオブジェクト
	 * @return サーブレットパス
	 */
	public static String getServletPath(final HttpServletRequest request) {
		if (isInclude(request)) {
			return String.class.cast(request.getAttribute("javax.servlet.include.servlet_path"));
		}
		return request.getServletPath();
	}

	/**
	 * インクルード判断
	 * @param request リクエストオブジェクト
	 * @return インクルードの場合 true を返す。
	 */
	public static boolean isInclude(final HttpServletRequest request) {
		return request.getAttribute("javax.servlet.include.request_uri") != null;
	}

	/**
	 * jsessionidなしURI
	 * @param val URI
	 * @return jsessionidなしURI
	 */
	public static String toPlainURI(final String val) {
		int loc = val.indexOf(';');
		if (0 <= loc) {
			return val.substring(0, loc);
		}
		return val;
	}

	/**
	 * GETメソッド判断
	 * @param method メソッド
	 * @return GETの場合 true を返す。
	 */
	public static boolean isGetMethod(final String method) {
		return "GET".equalsIgnoreCase(method) || "HEAD".equalsIgnoreCase(method);
	}

	/**
	 * POSTメソッド判断
	 * @param method メソッド
	 * @return POSTの場合 true を返す。
	 */
	public static boolean isPostMethod(final String method) {
		return "POST".equalsIgnoreCase(method);
	}

	/**
	 * エラー送信
	 * @param response レスポンス
	 */
	static void sendError(final HttpServletResponse response) {
		try {
			if (!response.isCommitted()) {
				response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
				response.flushBuffer();
			}
		} catch (IOException ex) {
			LogManager.getLogger().info(ex.getMessage());
		}
	}
}
